package com.mapforge.map.tilewriter;

import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;

import com.mapforge.map.Mask;
import com.mapforge.map.Tile;
import com.mapforge.map.TileLayer;
import com.mapforge.map.Tileset;
import com.mapforge.map.tools.undo.TilePoint;
import com.mapforge.selection.ComparablePoint;
import com.mapforge.selection.Selection;
import com.mapforge.selection.TileSelection;

/**
 * 
 * The {@link RepeatingSequentialTileWriter} class calculates which tile in
 * input will be written to the destination by repeating the input
 * {@link Selection} bounding rectangle across the destination {@link Selection}
 * bounding rectangle. Should there not exist a point within the input
 * {@link Selection} to match a corresponding point in the destination area,
 * that portion of the destination will not be altered.
 * 
 * Points written to in the destination must also pass a masking test, and be
 * within the {@link TileLayer} dimensions. Anything considered selected within
 * the {@link Mask} is valid for writing.
 * 
 * @see Mask
 * @see Selection
 * @author Will Morrison
 * 
 */
public class RepeatingSequentialTileWriter extends AbstractTileWriter {
	private Vector<Tile> tiles;

	public RepeatingSequentialTileWriter() {
		super();
		this.tiles = new Vector<Tile>();
	}

	public void previewWrite(Graphics2D g, TileSelection tileSelection,
			Selection destination) {
		TileLayer layer = getTileLayer();
		if (layer == null) {
			throw new NullPointerException("Attempting to write to null layer");
		} else if (layer.isLocked() || !layer.isVisible()) {
			throw new IllegalStateException(
					"Cannot write to layers which are not visible or are locked");
		} else {
			Rectangle selectionRect = tileSelection.getBoundingRectangle();
			Rectangle destinationRect = destination.getBoundingRectangle();
			if (selectionRect.width < 1 || selectionRect.height < 1) {
				throw new IllegalStateException("Missing tile selection");
			}

			Tileset tileset = getTileset();
			Iterator<? extends Point> destinationIT = destination.iterator();
			Point p;
			// get all points within write area
			while (destinationIT.hasNext()) {
				p = destinationIT.next();
				int x = selectionRect.x + (p.x - destinationRect.x)
						% selectionRect.width;
				int y = selectionRect.y + (p.y - destinationRect.y)
						% selectionRect.height;
				if (tileSelection.contains(x, y)) {
					Tile tile = tileset.getTileAt(x, y);
					tile.render(g, p.x * tileset.getTileWidth(), p.y
							* tileset.getTileHeight());
				}
			}
		}
	}

	public synchronized List<Tile> writeTiles(TileSelection input,
			Selection destination) {
		this.validateWriteState(input, destination);
		TileLayer layer = getTileLayer();
		Tileset tileset = this.getTileset();
		tiles.clear();
		Rectangle selectionRect = input.getBoundingRectangle();
		Rectangle destinationRect = destination.getBoundingRectangle();
		Iterator<ComparablePoint> it;

		System.out
				.println("sequential: Bounding rect of selection destination: "
						+ destination.getBoundingRectangle());
		System.out.println("Sequential: Tile selections bounding rectangle: "
				+ input.getBoundingRectangle());
		TilePoint p;
		int bottom = destinationRect.y + destinationRect.height;
		int right = destinationRect.x + destinationRect.width;
		int x, y;
		for (int y1 = destinationRect.y; y1 < bottom; y1 += selectionRect.height) {
			for (int x1 = destinationRect.x; x1 < right; x1 += selectionRect.width) {
				it = input.iterator();
				while (it.hasNext()) {
					p = (TilePoint) it.next();
					x = p.x - selectionRect.x + x1;
					y = p.y - selectionRect.y + y1;
					if (destination.contains(x, y)) {
						System.out.println(x + " " + y + " " + right + " "
								+ bottom);
						layer.setTile(x, y, p.tile);
					}
				}
			}
		}
		// get all points within write area
		/*
		 * while (destinationIT.hasNext()) { p = destinationIT.next(); int x =
		 * selectionRect.x + (p.x - destinationRect.x) % selectionRect.width;
		 * int y = selectionRect.y + (p.y - destinationRect.y) %
		 * selectionRect.height; // System.out.println("SequentialTileWriter " +
		 * // selectionRect // + " " + x + " " + y); if (input.contains(x, y)) {
		 * Tile tile = tileset.getTileAt(x, y); tiles.add(tile);
		 * layer.setTile(p.x, p.y, tile); } }
		 */
		return tiles;
	}
}